﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Text;
using System.Windows.Forms;
using System.Drawing;

namespace SCG.TurboSprite
{
    //Provides a scrolling ticker to a SpriteSurface with a queue of messages
    [ToolboxBitmap(typeof(Ticker), "Ticker")]
    public partial class Ticker : Component
    {
        //constructors
        public Ticker()
        {
            InitializeComponent();
        }
        public Ticker(IContainer container)
        {
            container.Add(this);
            InitializeComponent();
        }

        //The sprite surface on which to render the ticker
        public SpriteSurface SpriteSurface
        {
            get
            {
                return _surface;
            }
            set
            {
                if (!DesignMode)
                {
                    if (_surface != null)
                        _surface.AfterSpriteRender -= Render;
                }
                _surface = value;
                if (!DesignMode)
                {
                    if (_surface != null)
                        _surface.AfterSpriteRender += Render;
                }
            }
        }

        //Should the ticker be visible?
        public bool Visible
        {
            get
            {
                return _visible;
            }
            set
            {
                _visible = value;
            }
        }

        //The speed to scroll the ticker in pixels
        public int ScrollSpeed
        {
            get
            {
                return _scrollSpeed;
            }
            set
            {
                if (value > 0)
                {
                    _oldScrollSpeed = value;
                    _scrollSpeed = value;
                }
            }
        }

        //Should messages that roll off be re-qeued?
        public bool LoopMessages { get; set; }

        //Where to render the ticker
        public TickerRenderLocation Location { get; set; }

        //The font color
        public Color ForeColor
        {
            get
            {
                return _foreColor;
            }
            set
            {
                _foreColor = value;
                _brush = new SolidBrush(_foreColor);              
            }
        }

        //color of ticker background
        public Color BackColor
        {
            get
            {
                return _backColor;
            }
            set
            {
                _backColor = value;
                _backBrush = new SolidBrush(_backColor);
            }
        }

        //The font
        public Font Font
        {
            get
            {
                return _font;
            }
            set
            {
                _font = value;
                _h = -1;
            }
        }

        //pixels margin
        public int Margin
        {
            get
            {
                return _margin;
            }
            set
            {
                if (value >= 0)
                    _margin = value;
            }
        }

        //add a new message to the queue
        public void AddMessage(string message)
        {
            lock (_messages)
            {
                _messages.Enqueue(message);
            }
        }

        //clear messages
        public void Clear()
        {
            lock (_messages)
            {
                _messages.Clear();
            }
        }

        //start and stop the ticker
        public void Start()
        {
            ScrollSpeed = _oldScrollSpeed;
        }
        public void Stop()
        {
            ScrollSpeed = 0;
        }

        //private members
        private SpriteSurface _surface;
        private List<string> _visibleMessages = new List<string>();
        private List<int> _positions = new List<int>();
        private Queue<string> _messages = new Queue<string>();
        private bool _visible = true;
        private int _scrollSpeed = 5;
        private int _oldScrollSpeed = 5;
        private Color _foreColor = Color.White;
        private Brush _brush = Brushes.White;
        private Color _backColor = Color.Black;
        private Brush _backBrush = Brushes.Black;
        private Font _font = new Font("Microsoft Sans Serif", 8);
        private int _margin = 6;
        private int _h = -1;     

        //The rendering event handler
        private void Render(object sender, PaintEventArgs pe)
        {
            if (!Visible)
                return;
            if (_visibleMessages.Count == 0 && _messages.Count == 0)
                return;

            //measure test height
            if (_h == -1)
            {
                SizeF ts = pe.Graphics.MeasureString("X", _font);
                _h = (int)ts.Height + 2;
            }

            //draw a transparent rectangle for the ticker to move in
            int x = Margin;
            int w = SpriteSurface.Width - Margin * 2;
            int y = Location == TickerRenderLocation.Bottom ? SpriteSurface.Height - _h - Margin : Margin;
            pe.Graphics.FillRectangle(_backBrush, x, y, w, _h);
            pe.Graphics.SetClip(new Rectangle(x, y, w, _h));

            //render the visible messages
            int posIdx = 0;         
            foreach (string msg in _visibleMessages)
            {
                int textPos = _positions[posIdx];                
                pe.Graphics.DrawString(msg, _font, _brush, textPos, y + 1);

                //advance left
                textPos -= ScrollSpeed;
                _positions[posIdx] = textPos;
                posIdx++;
            }

            //remove messages that scroll off left edge
            if (_visibleMessages.Count > 0)
            {
                string check = _visibleMessages[0];
                int tw = (int)pe.Graphics.MeasureString(check, _font).Width;
                int tx = _positions[0];
                if (tx + tw < Margin)
                {
                    _visibleMessages.RemoveAt(0);
                    _positions.RemoveAt(0);

                    //re-use it?
                    if (LoopMessages)
                        AddMessage(check);
                }
            }

            //if there's room for a new message add it to visible list
            if (_messages.Count > 0)
            {
                bool roomForNew = true;
                if (_visibleMessages.Count > 0)
                {
                    int lastIdx = _visibleMessages.Count - 1;
                    string last = _visibleMessages[lastIdx];
                    int tw = (int)pe.Graphics.MeasureString(last, _font).Width;
                    int tx = _positions[lastIdx];
                    if (tw + tx > SpriteSurface.Width - Margin * 3)
                        roomForNew = false;
                }
                if (roomForNew)
                {
                    lock (_messages)
                    {
                        _visibleMessages.Add(_messages.Dequeue());
                    }
                    _positions.Add(SpriteSurface.Width - Margin);
                }
            }

            pe.Graphics.ResetClip();
        }
    }

    //The location to render the ticker
    public enum TickerRenderLocation { Top, Bottom }
}
